/*
 * Copyright (C) 2004-2005, Universidade Federal de Campina Grande. All rights reserved.
 */
package org.epanetgrid.otimizacao.ag;

import java.util.Arrays;
import java.util.List;

import org.epanetgrid.otimizacao.util.OtimizacaoUtil;
import org.jheuristics.Individual;
import org.jheuristics.ga.GAConfig;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.operators.Probability;
import org.jheuristics.ga.operators.Reproductor;
import org.jheuristics.ga.operators.reproductors.AbstractMutation;
import org.jheuristics.util.BitString;
import org.jheuristics.util.RandomGenerator;


/**
 * Vide documenta��o t�cnica para maiores detalhe de implementa��o.
 * @author Marcell Manfrin, marcell at ourgrid.org, 2005
 */
public class MutacaoNormal extends AbstractMutation {
	private double porcentagemDeMutacao;

	/**
	 *
	 * @param probability Probabilidade de muta��o normal
	 * @param maximoDeTentativasInvalidas N�mero m�ximo de tentativas
	 * @param porcentagemDeMutacao Porcentagens de bombas mutadas
	 */
	public MutacaoNormal(Probability probability, int maximoDeTentativasInvalidas, double porcentagemDeMutacao) {
		super(probability, maximoDeTentativasInvalidas);
		setPorcentagemDeMutacao(porcentagemDeMutacao);
	}

	/**
	 *
	 * @param decoretedReproductor Reprodutor
	 * @param probability Probabilidade de muta��o normal
	 * @param maximoDeTentativasInvalidas N�mero m�ximo de tentativas
	 * @param porcentagemDeMutacao Porcentagens de bombas mutadas
	 */
	public MutacaoNormal(Reproductor decoretedReproductor, Probability probability, int maximoDeTentativasInvalidas, double porcentagemDeMutacao) {
		super(decoretedReproductor, probability, maximoDeTentativasInvalidas);
		setPorcentagemDeMutacao(porcentagemDeMutacao);
	}

	/**
	 *
	 * @return Porcentagem de bombas mutadas
	 */
	public double getPorcentagemDeMutacao() {
		return porcentagemDeMutacao;
	}

	/**
	 * @param porcentagemDeMutacao Novo valor para a porcentagem de bombas mutadas
	 */
	public void setPorcentagemDeMutacao(double porcentagemDeMutacao) {
		if (0 > porcentagemDeMutacao || 1 < porcentagemDeMutacao) {
			throw new IllegalArgumentException();
		}
		this.porcentagemDeMutacao = porcentagemDeMutacao;
	}

	/**
	 *
	 * @param parent Indiv�duo que sofrer� muta��o
	 * @param status Status do AG
	 * @param config Configura��o atual do ag
	 * @return indiv�duo mutado
	 * @see org.jheuristics.ga.operators.reproductors.AbstractMutation#mutate(org.jheuristics.Individual, org.jheuristics.ga.GAStatus, org.jheuristics.ga.GAConfig)
	 */
	protected Individual mutate(Individual parent, GAStatus status, GAConfig config) {
		StringBuffer buffer = new StringBuffer();
    	buffer.append("Verificando probabilidade de muta��o:");
    	buffer.append("\n");
		if (getProbability().check(status, config)) {
			List<BitString> gens = (List<BitString>) parent.getGens();
			RandomGenerator random = config.getRandomGenerator();
			int posicao = random.nextInt(gens.size());
			BitString genOrignal = gens.get(posicao);
			BitString genMutado = mutate(new BitString(genOrignal), random);
			
			buffer.append("\nCromossomo enviado para Muta��o normal: ");
			buffer.append(parent);
			buffer.append("\n");
			
			if (!genOrignal.equals(genMutado)) {
				List<BitString> genesMutados = Arrays.asList(montagemDosGenes(gens, genMutado, posicao));
				buffer.append("Cromossomo mutado: ");
				buffer.append(OtimizacaoUtil.gensToString(genesMutados));
				buffer.append("\n");
	    		logger.debug(buffer);
				
				return config.getIndividualsFactory().createSpecificIndividual(genesMutados, config);
			}
		}else {
			buffer.append("Muta��o N�o ser� efetuada");
    		buffer.append("\n");
    		logger.debug(buffer);
		}
		return null;
	}

	/**
	 * Efetua muta��o bit a bit no gen
	 * @param gen Gene a ser mutado
	 * @param random Gerador de numeros randomicos
	 * @return genes mutados
	 */
	protected BitString mutate(BitString gen, RandomGenerator random) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < Math.round(Math.ceil(getPorcentagemDeMutacao() * gen.length())); i++) {
			int posicao = random.nextInt(gen.length()); 
			gen.flip(posicao);
			buffer.append("\nMuta��o efetuada no bit: ");
			buffer.append(posicao+1);
		}
		
		buffer.append("\n");
		logger.debug(buffer);
		
		return gen;
	}

	/**
	 *
	 * @param genesOriginais Gene original
	 * @param genMutado Parte do Gene mutado
	 * @param posicao Posi��o que o geneMutado ser� inserido
	 * @return Resultado da substitui��o do geneMutado no genesOriginais
	 */
	protected BitString[] montagemDosGenes(List genesOriginais, BitString genMutado, int posicao) {
		BitString[] genesMutados = new BitString[genesOriginais.size()];
		for (int i = 0; i < posicao; i++) {
			genesMutados[i] = new BitString((BitString) genesOriginais.get(i));
		}
		genesMutados[posicao] = genMutado;
		for (int i = posicao+1; i < genesMutados.length; i++) {
			genesMutados[i] = new BitString((BitString) genesOriginais.get(i));
		}
		return genesMutados;
	}

}
